package mmc.cell;

import java.util.ArrayList;
import java.util.List;

/**
 * This class manages the description of block-types. Changes in this class
 * may lead to block-ids changing and therefore may disable save-files or textures.
 */
public class Block {
	private static final Block[] BLOCK = new Block[256];
	
	private static final int BLOCK_ID_OFFSET = Byte.MIN_VALUE;
	private static final int BLOCK_NONSOLID_BORDER = BLOCK_ID_OFFSET+16;
	private static final int BLOCK_TRANSLUCENT_BORDER = BLOCK_ID_OFFSET+32;
	
	public static final byte AIR_COUNT;
	
	public static final byte GRASS;
	public static final byte EARTH;
	public static final byte STONE;
	public static final byte AIR_SUN;
	public static final byte AIR_DARKNESS;
	
	/**
	 * initializer for static final members.
	 */
	static{
		AIR_COUNT = 12;
		
		List<Block> toAdd = new ArrayList<Block>();
		
		for(int i=0;i<AIR_COUNT;i++){
			toAdd.add(new Block("Air"+i, true, false));
		}
		
		toAdd.add(new Block("Grass", false, true));
		toAdd.add(new Block("Earth", false, true));
		toAdd.add(new Block("Stone", false, true));
		
		for(Block b : toAdd){
			int free = BLOCK_ID_OFFSET;
			
			for(;;free++){
				if(BLOCK[free-BLOCK_ID_OFFSET]==null &&
				   b.solid==isSolid((byte)free) &&
				   b.translucent==isTranslucent((byte)free)){
					break;
				}
				
				if(free>Byte.MAX_VALUE)
					throw new RuntimeException("no matching id found!");
			}
			
			BLOCK[free-BLOCK_ID_OFFSET] = b;
		}
		
		AIR_SUN = getId("Air0");
		AIR_DARKNESS = getId("Air"+(AIR_COUNT-1));
		GRASS = getId("Grass");
		EARTH = getId("Earth");
		STONE = getId("Stone");
	}
	
	/**
	 * Name of this Blocktype (e.g. Air, Grass, Earth).
	 */
	private String name;
	
	/**
	 * translucency - compare {@link #isTranslucent(byte)}
	 */
	private boolean translucent;
	
	/**
	 * solid - compare {@link #isSolid(byte)}
	 */
	private boolean solid;
	
	/**
	 * Creates a new Blocktype.
	 * 
	 * @param name name of this block
	 * @param translucent compare  {@link #isTranslucent(byte)}
	 * @param solid compare {@link #isSolid(byte)}
	 */
	private Block(String name, boolean translucent, boolean solid){
		this.name = name;
		this.translucent = translucent;
		this.solid = solid;
	}
	
	/**
	 * Returns the Block-object with the given id.
	 * 
	 * @param id id of the block-type
	 * @return a Block
	 */
	public static Block getBlock(byte id){
		return BLOCK[id-BLOCK_ID_OFFSET];
	}
	
	/**
	 * Returns true if, and only if, this block is translucent. A block
	 * is translucent if Light can pass through it. Non-solid blocks
	 * are always translucent. 
	 * 
	 * @param id of the block
	 * @return true if, and only if, this block is translucent
	 */
	public static boolean isTranslucent(byte id){
		return id < BLOCK_TRANSLUCENT_BORDER;
	}
	
	/**
	 * Returns true if, and only if, this block is solid. A block is non-solid,
	 * if light and bigger objects can pass through it. Non-solid block are always 
	 * translucent, although solid-blocks may be translucent too.
	 * 
	 * @param id of the block
	 * @return true if, and only if, this block is solid
	 */
	public static boolean isSolid(byte id){
		return id >= BLOCK_NONSOLID_BORDER;
	}
	
	/**
	 * Searches the internal BLOCK-array for a block with the given name. This method
	 * is designed for use in the static initializer only (it is inefficient).
	 * 
	 * @param name to be searched for (case-sensitive)
	 * @return id of the block
	 */
	private static byte getId(String name){
		for(int id=0;id<256;id++){
			Block b = BLOCK[id];
			
			if(b!=null && b.name.equals(name)){
				return (byte)(id+BLOCK_ID_OFFSET);
			}
		}
		
		throw new RuntimeException("could not locate "+name);
	}
	
	/**
	 * Returns the name of this block-type.
	 */
	public String toString(){
		return name;
	}
}
